home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / programr / dpmigcc5.zip / RSX / SOURCE / FPU-EMU / REG_ADD_.C < prev    next >
C/C++ Source or Header  |  1994-05-27  |  8KB  |  319 lines

  1. /*---------------------------------------------------------------------------+
  2.  |  reg_add_sub.c                                                            |
  3.  |                                                                           |
  4.  | Functions to add or subtract two registers and put the result in a third. |
  5.  |                                                                           |
  6.  | Copyright (C) 1992,1993                                                   |
  7.  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
  8.  |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  9.  |                                                                           |
  10.  |                                                                           |
  11.  +---------------------------------------------------------------------------*/
  12.  
  13. /*---------------------------------------------------------------------------+
  14.  | For each function, the destination may be any FPU_REG, including one of   |
  15.  | the source FPU_REGs.                                                      |
  16.  +---------------------------------------------------------------------------*/
  17.  
  18. #include "exception.h"
  19. #include "reg_constant.h"
  20. #include "fpu_emu.h"
  21. #include "control_w.h"
  22. #include "fpu_system.h"
  23.  
  24.  
  25. int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
  26. {
  27.   char saved_sign = dest->sign;
  28.   int diff;
  29.   
  30.   if ( !(a->tag | b->tag) )
  31.     {
  32.       /* Both registers are valid */
  33.       if (!(a->sign ^ b->sign))
  34.     {
  35.       /* signs are the same */
  36.       dest->sign = a->sign;
  37.       if ( reg_u_add(a, b, dest, control_w) )
  38.         {
  39.           dest->sign = saved_sign;
  40.           return 1;
  41.         }
  42.       return 0;
  43.     }
  44.       
  45.       /* The signs are different, so do a subtraction */
  46.       diff = a->exp - b->exp;
  47.       if (!diff)
  48.     {
  49.       diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
  50.       if (!diff)
  51.         {
  52.           diff = a->sigl > b->sigl;
  53.           if (!diff)
  54.         diff = -(a->sigl < b->sigl);
  55.         }
  56.     }
  57.       
  58.       if (diff > 0)
  59.     {
  60.       dest->sign = a->sign;
  61.       if ( reg_u_sub(a, b, dest, control_w) )
  62.         {
  63.           dest->sign = saved_sign;
  64.           return 1;
  65.         }
  66.     }
  67.       else if ( diff == 0 )
  68.     {
  69. #ifdef DENORM_OPERAND
  70.       if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
  71.           denormal_operand() )
  72.         return 1;
  73. #endif DENORM_OPERAND
  74.       reg_move(&CONST_Z, dest);
  75.       /* sign depends upon rounding mode */
  76.       dest->sign = ((control_w & CW_RC) != RC_DOWN)
  77.         ? SIGN_POS : SIGN_NEG;
  78.     }
  79.       else
  80.     {
  81.       dest->sign = b->sign;
  82.       if ( reg_u_sub(b, a, dest, control_w) )
  83.         {
  84.           dest->sign = saved_sign;
  85.           return 1;
  86.         }
  87.     }
  88.       return 0;
  89.     }
  90.   else
  91.     {
  92.       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
  93.     { return real_2op_NaN(a, b, dest); }
  94.       else if (a->tag == TW_Zero)
  95.     {
  96.       if (b->tag == TW_Zero)
  97.         {
  98.           char different_signs = a->sign ^ b->sign;
  99.           /* Both are zero, result will be zero. */
  100.           reg_move(a, dest);
  101.           if (different_signs)
  102.         {
  103.           /* Signs are different. */
  104.           /* Sign of answer depends upon rounding mode. */
  105.           dest->sign = ((control_w & CW_RC) != RC_DOWN)
  106.             ? SIGN_POS : SIGN_NEG;
  107.         }
  108.         }
  109.       else
  110.         {
  111. #ifdef DENORM_OPERAND
  112.           if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
  113.           denormal_operand() )
  114.         return 1;
  115. #endif DENORM_OPERAND
  116.           reg_move(b, dest);
  117.         }
  118.       return 0;
  119.     }
  120.       else if (b->tag == TW_Zero)
  121.     {
  122. #ifdef DENORM_OPERAND
  123.       if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
  124.           denormal_operand() )
  125.         return 1;
  126. #endif DENORM_OPERAND
  127.       reg_move(a, dest); return 0;
  128.     }
  129.       else if (a->tag == TW_Infinity)
  130.     {
  131.       if (b->tag != TW_Infinity)
  132.         {
  133. #ifdef DENORM_OPERAND
  134.           if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
  135.           denormal_operand() )
  136.         return 1;
  137. #endif DENORM_OPERAND
  138.           reg_move(a, dest); return 0;
  139.         }
  140.       if (a->sign == b->sign)
  141.         {
  142.           /* They are both + or - infinity */
  143.           reg_move(a, dest); return 0;
  144.         }
  145.       return arith_invalid(dest);    /* Infinity-Infinity is undefined. */
  146.     }
  147.       else if (b->tag == TW_Infinity)
  148.     {
  149. #ifdef DENORM_OPERAND
  150.       if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
  151.           denormal_operand() )
  152.         return 1;
  153. #endif DENORM_OPERAND
  154.       reg_move(b, dest); return 0;
  155.     }
  156.     }
  157. #ifdef PARANOID
  158.   EXCEPTION(EX_INTERNAL|0x101);
  159. #endif
  160.   return 1;
  161. }
  162.  
  163.  
  164. /* Subtract b from a.  (a-b) -> dest */
  165. int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
  166. {
  167.   char saved_sign = dest->sign;
  168.   int diff;
  169.  
  170.   if ( !(a->tag | b->tag) )
  171.     {
  172.       /* Both registers are valid */
  173.       diff = a->exp - b->exp;
  174.       if (!diff)
  175.     {
  176.       diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
  177.       if (!diff)
  178.         {
  179.           diff = a->sigl > b->sigl;
  180.           if (!diff)
  181.         diff = -(a->sigl < b->sigl);
  182.         }
  183.     }
  184.  
  185.       switch (a->sign*2 + b->sign)
  186.     {
  187.     case 0: /* P - P */
  188.     case 3: /* N - N */
  189.       if (diff > 0)
  190.         {
  191.           /* |a| > |b| */
  192.           dest->sign = a->sign;
  193.           if ( reg_u_sub(a, b, dest, control_w) )
  194.         {
  195.           dest->sign = saved_sign;
  196.           return 1;
  197.         }
  198.           return 0;
  199.         }
  200.       else if ( diff == 0 )
  201.         {
  202. #ifdef DENORM_OPERAND
  203.           if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
  204.           denormal_operand() )
  205.         return 1;
  206. #endif DENORM_OPERAND
  207.           reg_move(&CONST_Z, dest);
  208.           /* sign depends upon rounding mode */
  209.           dest->sign = ((control_w & CW_RC) != RC_DOWN)
  210.         ? SIGN_POS : SIGN_NEG;
  211.         }
  212.       else
  213.         {
  214.           dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
  215.           if ( reg_u_sub(b, a, dest, control_w) )
  216.         {
  217.           dest->sign = saved_sign;
  218.           return 1;
  219.         }
  220.         }
  221.       break;
  222.     case 1: /* P - N */
  223.       dest->sign = SIGN_POS;
  224.       if ( reg_u_add(a, b, dest, control_w) )
  225.         {
  226.           dest->sign = saved_sign;
  227.           return 1;
  228.         }
  229.       break;
  230.     case 2: /* N - P */
  231.       dest->sign = SIGN_NEG;
  232.       if ( reg_u_add(a, b, dest, control_w) )
  233.         {
  234.           dest->sign = saved_sign;
  235.           return 1;
  236.         }
  237.       break;
  238.     }
  239.       return 0;
  240.     }
  241.   else
  242.     {
  243.       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
  244.     { return real_2op_NaN(b, a, dest); }
  245.       else if (b->tag == TW_Zero)
  246.     { 
  247.       if (a->tag == TW_Zero)
  248.         {
  249.           char same_signs = !(a->sign ^ b->sign);
  250.           /* Both are zero, result will be zero. */
  251.           reg_move(a, dest); /* Answer for different signs. */
  252.           if (same_signs)
  253.         {
  254.           /* Sign depends upon rounding mode */
  255.           dest->sign = ((control_w & CW_RC) != RC_DOWN)
  256.             ? SIGN_POS : SIGN_NEG;
  257.         }
  258.         }
  259.       else
  260.         {
  261. #ifdef DENORM_OPERAND
  262.           if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
  263.           denormal_operand() )
  264.         return 1;
  265. #endif DENORM_OPERAND
  266.           reg_move(a, dest);
  267.         }
  268.       return 0;
  269.     }
  270.       else if (a->tag == TW_Zero)
  271.     {
  272. #ifdef DENORM_OPERAND
  273.       if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
  274.           denormal_operand() )
  275.         return 1;
  276. #endif DENORM_OPERAND
  277.       reg_move(b, dest);
  278.       dest->sign ^= SIGN_POS^SIGN_NEG;
  279.       return 0;
  280.     }
  281.       else if (a->tag == TW_Infinity)
  282.     {
  283.       if (b->tag != TW_Infinity)
  284.         {
  285. #ifdef DENORM_OPERAND
  286.           if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
  287.           denormal_operand() )
  288.         return 1;
  289. #endif DENORM_OPERAND
  290.           reg_move(a, dest); return 0;
  291.         }
  292.       /* Both args are Infinity */
  293.       if (a->sign == b->sign)
  294.         {
  295.           /* Infinity-Infinity is undefined. */
  296.           return arith_invalid(dest);
  297.         }
  298.       reg_move(a, dest);
  299.       return 0;
  300.     }
  301.       else if (b->tag == TW_Infinity)
  302.     {
  303. #ifdef DENORM_OPERAND
  304.       if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
  305.           denormal_operand() )
  306.         return 1;
  307. #endif DENORM_OPERAND
  308.       reg_move(b, dest);
  309.       dest->sign ^= SIGN_POS^SIGN_NEG;
  310.       return 0;
  311.     }
  312.     }
  313. #ifdef PARANOID
  314.   EXCEPTION(EX_INTERNAL|0x110);
  315. #endif
  316.   return 1;
  317. }
  318.  
  319.